#ifndef EPUBDUKPT_CRYPTO_DES_HPP
#define EPUBDUKPT_CRYPTO_DES_HPP

SJR_NAMESPACE_BEGIN

class EpubCryptoDes final : public EpubCryptoBasic {
public:
  class ScheduleDes final : public Schedule {
  public:
    constexpr static int KEY_BLOCK_SIZE = 8;

    explicit ScheduleDes(std::string const &in) : Schedule(), mKey(KEY_BLOCK_SIZE) {
      auto &&fmtKey = EpubFormat::array(in);
      ScheduleDes::keySet(mKey, fmtKey.data(), static_cast<int>(fmtKey.size()));
      this->setKey(mKey.data(), static_cast<int>(mKey.size()));
    }

    explicit ScheduleDes(std::vector<unsigned char> &in) : Schedule(), mKey(KEY_BLOCK_SIZE) {
      ScheduleDes::keySet(mKey, in.data(), static_cast<int>(in.size()));
      this->setKey(mKey.data(), static_cast<int>(mKey.size()));
    }

    ScheduleDes(unsigned char const *in, int inLen) : Schedule(), mKey(KEY_BLOCK_SIZE) {
      ScheduleDes::keySet(mKey, in, inLen);
      this->setKey(mKey.data(), static_cast<int>(mKey.size()));
    }

  private:
    std::vector<unsigned char> mKey;

    static void keySet(std::vector<unsigned char> &local, unsigned char const *in, int inLen) {
      auto &&autLen = local.size() > inLen ? inLen : local.size();
      memcpy(&local[0], in, autLen);
    }
  };

  auto blockSize(Schedule *key) -> int override {
    return key->size();
  }

  auto encrypt(Schedule *key, unsigned char const *in, unsigned char *out) -> bool override {
    auto &&scheKey = DES_key_schedule();
    DES_set_key_unchecked(reinterpret_cast<DES_cblock *>(&(*key)[0]), &scheKey);
    DES_ecb_encrypt(reinterpret_cast<DES_cblock *>(const_cast<unsigned char *>(in)),
      reinterpret_cast<DES_cblock *>(out), &scheKey, DES_ENCRYPT);
    return true;
  }

  auto decrypt(Schedule *key, unsigned char const *in, unsigned char *out) -> bool override {
    auto &&scheKey = DES_key_schedule();
    DES_set_key_unchecked(reinterpret_cast<DES_cblock *>(&(*key)[0]), &scheKey);
    DES_ecb_encrypt(reinterpret_cast<DES_cblock *>(const_cast<unsigned char *>(in)),
      reinterpret_cast<DES_cblock *>(out), &scheKey, DES_DECRYPT);
    return true;
  }
};

SJR_NAMESPACE_END

#endif//EPUBDUKPT_CRYPTO_DES_HPP
